# -*- coding: utf-8 -*-
"""A simple example for conversation between user and assistant agent."""
import concurrent
import copy
import threading
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
import json
import random
import time
from typing import List

import agentscope
from agentscope.agents import DialogAgent
from agentscope.agents.user_agent import UserAgent
from agentscope.message import Msg
from agentscope.pipelines.functional import sequentialpipeline
from examples.opinion_simulation.Agent import Agent
from scipy.stats import pearsonr
import numpy as np
import matplotlib.pyplot as plt

from examples.opinion_simulation.conclusionAgent import conclusionAgent

AgentCount=30

def run(topic: str) -> None:
    """A basic conversation demo"""
    agentscope.init(
        model_configs="./configs/model_configs.json",
        agent_configs="./configs/agent_configs.json",
        project="Multi-Agent Conversation",
        save_api_invoke=True,
    )
    filename="D:/agentscope/examples/opinion_simulation/profiles.json"
    agent_data=read_json_data(filename)
    # 初始化 agent 列表
    agents = []
    # 遍历并打印每个代理的信息
    if agent_data:
        for agent in agent_data:
            prompt_str=generate_basic_prompt(agent)#TODO 把bigfive和邻接矩阵弄一起
            ag=Agent(
                name=agent['name'],
                model_config_name="my-qwen-plus-latest",  # replace by your model config name
                topic=topic,
                sys_prompt=prompt_str,
            )
            agents.append(ag)

    clag = conclusionAgent(
        name="col",
        model_config_name="my-qwen-plus-latest",  # replace by your model config name
    )

    x=None
    # 存储每轮的信念值
    beliefs_agents = [[],[],[],[],[],[],[],[],[],[],
                      [],[],[],[],[],[],[],[],[],[],
                      [],[],[],[],[],[],[],[],[],[]]#30个

    for j in range(len(agents)):
        print(f"agent{j}:{agents[j].belief} ")#输出初始信念值
    mask=[17,18,14]
    # 示例：小世界模型
    adjacency_matrix = [
 [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
 [0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 1],
 [1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,0, 0, 0, 0, 0, 0],
 [0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0,0, 1, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0,0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1,0, 0, 0, 1, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,1, 0, 0, 0, 0, 0],
 [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,1, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,0, 1, 1, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,1, 0, 1, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1, 1, 0, 1, 1, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,0, 0, 1, 0, 1, 0],
 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 1, 1, 0, 1],
 [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 1, 0]
    ]

    print("邻接矩阵小世界结构）:")
    print(adjacency_matrix)

    def process_agent(agent):
        # 原子性操作封装为函数
        agent_memory = agent.memory.get_memory()
        new_memory = clag.conclusion(agent.long_term, agent_memory)
        agent.update_memory_and_dialog(new_memory)

    i = 0
    while i!=30:#30轮
        num_agents = len(beliefs_agents)  # 获取代理总数
        count = 0
        count_lock = threading.Lock()  # 用于保护计数器的锁
        for t in range(num_agents):#选择说话的agent
            if i==10 and(t in mask):
                continue
            if agents[t].receive_queue.empty():
                msg = agents[t](None)#这是需要发送的消息
            else:
                msg_list: List[Msg] = []
                while not agents[t].receive_queue.empty():
                    item = agents[t].receive_queue.get_nowait()
                    if isinstance(item, Msg):  # 类型保护
                        msg_list.append(item)
                    else:
                        pass
                msg= agents[t](msg_list)#TODO 队列中所有元素
            # threads = []
            for k in range(num_agents):#选择接收的agnet
                if(adjacency_matrix[t][k]==1):
                    # # 为每个接收者创建线程，固定当前msg的值
                    # thread = threading.Thread(
                    #     target=lambda agent, msg: agent(msg),
                    #     args=(agents[k], msg)  # 明确传递当前msg的副本
                    # )
                    # threads.append(thread)
                    # thread.start()
                    agents[k].receive_queue.put(msg)


        if i%5==0 and i!=0:
            # 创建线程池（建议线程数=逻辑线程数 8）
            with ThreadPoolExecutor(max_workers=8) as executor:
                # 提交所有任务并等待完成
                futures = [executor.submit(process_agent, agent) for agent in agents]
                for future in concurrent.futures.as_completed(futures):
                    future.result()  # 可在此处理异常
            # for j in range(num_agents):
            #     agent_memory=agents[j].memory.get_memory()
            #     new_mempry=clag.conclusion(agents[j].long_term,agent_memory)
            #     agents[j].update_memory_and_dialog(new_mempry)

        for j in range(num_agents):
            beliefs_agents[j].append(agents[j].belief)
        for j in range(num_agents):
            print(f"agent{j}:{agents[j].belief} ")
        print("\n")
        i=i+1

    #把belief转化为np数组
    # 转换为NumPy数组
    beliefs_np = np.array(beliefs_agents)  # 形状为 (30, 30)
    adjacency_matrix_np = np.array(adjacency_matrix)
    # 打印结果（设置打印格式保留两位小数）
    np.set_printoptions(precision=2)
    print("生成的信念值数组形状:", beliefs_np.shape)
    print("数组内容:\n", beliefs_agents)
    # 获取当前时间
    now = datetime.now()
    # 方法1：通过格式化字符串直接截断至分钟
    current_minute_str = now.strftime("%Y-%m-%d %H:%M")
    print("格式化截断:", current_minute_str)  # 输出: 2025-03-27 21:05

    save_matrix_pretty(matrix=beliefs_agents,filename="实验结果.txt")
    #TODO 打印不行
    p = calculate_polarization(beliefs_np)
    print(p)

    g = calculate_global_disagreement(adjacency_matrix_np, beliefs_np)
    print(g)

    n = calculate_nci(adjacency_matrix_np, beliefs_np)
    print(n)
    turn=31#轮数+1
    # 设置中文字体（确保你的系统支持中文字体，否则会报错）
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 例如使用黑体
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
    # 创建画布和子图
    plt.figure(figsize=(12, 8))

    # 绘制极化度 (Pz)
    plt.subplot(3, 1, 1)#子图1
    plt.plot(range(1, turn), p, marker='o', linestyle='-', color='blue', label='极化度 (Pz)')
    plt.xlabel('轮次')
    plt.ylabel('极化度')
    plt.title('极化度 (Pz) 随轮次变化')
    plt.grid(True)
    plt.legend()
    plt.ylim(0, 1)  # 固定纵坐标范围

    # 绘制全局分歧度 (DG)
    plt.subplot(3, 1, 2)#子图2
    plt.plot(range(1, turn), g, marker='s', linestyle='--', color='red', label='全局分歧度 (DG)')
    plt.xlabel('轮次')
    plt.ylabel('分歧度')
    plt.title('全局分歧度 (DG) 随轮次变化')
    plt.grid(True)
    plt.legend()


    # 绘制归一化聚类指数 (NCI)
    plt.subplot(3, 1, 3)#子图3
    plt.plot(range(1, turn), n, marker='^', linestyle='-.', color='green', label='归一化聚类指数 (NCI)')
    plt.xlabel('轮次')
    plt.ylabel('NCI')
    plt.title('归一化聚类指数 (NCI) 随轮次变化')
    plt.grid(True)
    plt.legend()


    # 调整布局并显示
    plt.tight_layout()
    plt.savefig(f'指标变化趋势-{topic[:5]}.png', dpi=300)  # 保存为PNG图片
    plt.show()

    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    # 创建画布和坐标系
    plt.figure(figsize=(12, 6))

    # 在同一坐标系中绘制三条曲线
    plt.plot(range(1, turn), p, marker='o', linestyle='-', color='blue', label='极化度 (Pz)')
    plt.plot(range(1, turn), g, marker='s', linestyle='--', color='red', label='全局分歧度 (DG)')
    plt.plot(range(1, turn), n, marker='^', linestyle='-.', color='green', label='归一化聚类指数 (NCI)')

    # 统一设置坐标轴和标签
    plt.xlabel('轮次')
    plt.ylabel('指标值')
    plt.title('极化度、全局分歧度与NCI随轮次变化对比')

    plt.grid(True)
    plt.legend()

    # 调整布局并保存
    plt.tight_layout()
    plt.savefig(f'指标对比-{topic[:10]}-{current_minute_str}.png', dpi=300)
    plt.show()


def read_json_data(file_path: str):
        """
        从指定的 JSON 文件中读取数据并返回相应的数据结构。

        :param file_path: JSON 文件的路径
        :return: 返回读取的 JSON 数据结构（通常为列表或字典）
        """
        try:
            # 打开并读取 JSON 文件
            with open(file_path, 'r', encoding='utf-8') as file:
                data = json.load(file)
            return data
        except Exception as e:
            # 捕获文件打开或解析时的错误
            print(f"Error reading the file {file_path}: {e}")
            return None
def generate_basic_prompt(profile):
        """
        生成基础的填充内容（不包含记忆部分）

        :param profile: 用户的profile信息
        :return: 填充后的字符串
        """
        name = profile["name"]
        age = profile["age"]
        gender = profile["gender"]
        education = profile["education"]
        big5 = profile["big5"]

        prompt = f"你的姓名是{name},你是一个{gender}的{age}岁{education}教育程度者。\n" \
                 f"根据你的性格特质（开放性{big5['O']}, 尽责性{big5['C']}, 外向性{big5['E']}, \n" \
                 f"宜人性{big5['A']}, 神经质{big5['N']}）"

        return prompt


def calculate_polarization(beliefs_np):
    """
    计算每轮的极化度 (Pz)，返回形状为 (T,) 的数组

    参数:
        beliefs_np: NumPy数组，形状为 (N, T)，N是代理数，T是轮次数

    返回值:
        polarization_per_round: 每轮的极化度，形状为 (T,)
    """
    mean_belief_per_round = np.mean(beliefs_np, axis=0)  # 按轮次计算均值，形状(T,)
    polarization_per_round = np.mean((beliefs_np - mean_belief_per_round) ** 2, axis=0)  # 按轮次计算方差，形状(T,)
    return polarization_per_round


def calculate_global_disagreement(adjacency_matrix, beliefs_np):
    """
    计算每轮的全局分歧度 (DG)，返回形状为 (T,) 的数组

    参数:
        adjacency_matrix: 邻接矩阵，形状为 (N, N)
        beliefs_np: NumPy数组，形状为 (N, T)，N是代理数，T是轮次数

    返回值:
        dg_per_round: 每轮的全局分歧度，形状为 (T,)
    """
    N, T = beliefs_np.shape
    dg_per_round = np.zeros(T)  # 初始化每轮的DG值

    for t in range(T):  # 遍历每一轮
        total_dg = 0.0
        for i in range(N):  # 遍历每个代理
            neighbors = np.where(adjacency_matrix[i] == 1)[0]  # 代理i的邻居
            if len(neighbors) == 0:
                continue
            # 计算代理i在第t轮与邻居的信念差异的均值
            diff = np.mean(np.abs(beliefs_np[i, t] - beliefs_np[neighbors, t]))
            total_dg += diff
        dg_per_round[t] = total_dg  # 存储第t轮的DG值

    return dg_per_round


def calculate_nci(adjacency_matrix, beliefs_np):
    """
    计算每轮的归一化聚类指数 (NCI)，返回形状为 (T,) 的数组

    参数:
        adjacency_matrix: 邻接矩阵，形状为 (N, N)
        beliefs_np: NumPy数组，形状为 (N, T)，T >= 2

    返回值:
        nci_per_round: 每轮的NCI值，形状为 (T,)

    注意:
        - 如果 T < 2，无法计算相关系数，会抛出异常
        - 每个轮次的NCI是基于当前轮及之前所有轮次的数据计算的
    """
    N, T = beliefs_np.shape
    nci_per_round = np.zeros(T)

    for t in range(T):
        # 需要至少2轮数据计算相关系数
        if t < 1:
            nci_per_round[t] = 0.0  # 第0轮无法计算，设为0
            continue

        # 提取从第0轮到第t轮的信念值（形状 (N, t+1)）
        cumulative_beliefs = beliefs_np[:, :t + 1]

        nci_values = []
        for i in range(N):
            neighbors = np.where(adjacency_matrix[i] == 1)[0]
            if len(neighbors) == 0:
                nci_values.append(0.0)
                continue

            # 计算代理i与每个邻居的相关系数
            correlations = []
            for j in neighbors:
                r, _ = pearsonr(cumulative_beliefs[i, :], cumulative_beliefs[j, :])
                correlations.append(r)

            nci_values.append(np.mean(correlations))

        nci_per_round[t] = np.mean(nci_values)  # 存储第t轮的NCI

    return nci_per_round

def save_matrix_pretty(matrix, filename, indent=4):
    with open(filename, 'w') as f:
        f.write('[\n')  # 起始大括号
        for i, row in enumerate(matrix):
            # 生成带有缩进和逗号的行
            line = ' ' * indent + '[' + ', '.join(map(str, row)) + ']'
            # 除最后一行外，其他行末尾加逗号
            if i != len(matrix) - 1:
                line += ','
            f.write(line + '\n')
        f.write(']')  # 结束大括号

if __name__ == '__main__':
    t=["我们现在在进行主题为：大数据时代，人获得越来越自由",
       "我们现在在进行主题为：以成败论英雄是否可取",
       "我们现在在进行主题为：顺境还是逆境更利于人成长",
       "我们现在在进行主题为：选择爱我的人比我爱的人更好",
       "我们现在在进行主题为：政府的公信力已经失效，我们应该怀疑政府",
       """是否应立法限制社交媒体算法推荐以减少信息茧房？
支持观点：算法推荐加剧观点极化，限制可促进信息多样性（如欧盟《数字服务法》案例） 。
用户应有自主选择权，避免被单一内容过度影响。
反对观点：算法优化提升用户体验，强制干预违背市场自由原则。
技术手段难以精准界定“信息茧房”，立法可能抑制创新。
        """]

    # 记录开始时间
    start_time = time.time()
    run(t[5])
    # 计算耗时（秒）
    elapsed_time = time.time() - start_time

    print(f"执行耗时: {elapsed_time:.4f} 秒")

